package com.fish.parser;

import com.fish.antlrlistener.SomeLanguageBaseListener;
import com.fish.antlrlistener.SomeLanguageLexer;
import com.fish.antlrlistener.SomeLanguageParser;
import com.fish.parser.lang.Class;
import com.fish.parser.lang.Instruction;
import com.fish.parser.lang.Method;
import org.antlr.v4.runtime.*;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by jlutt on 2018-01-22.
 *
 * @author jlutt
 */
public class ListenerOrientedParser {

  public Class parse(String code) {
    CharStream charStream = CharStreams.fromString(code);
    SomeLanguageLexer lexer = new SomeLanguageLexer(charStream);
    TokenStream tokens = new CommonTokenStream(lexer);
    SomeLanguageParser parser = new SomeLanguageParser(tokens);

    ClassListener classListener = new ClassListener();
    parser.classDeclaration().enterRule(classListener);
    return classListener.getParsedClass();
  }

  class InstructionListener extends SomeLanguageBaseListener {
    private Collection<Instruction> instructions;

    public InstructionListener() {
      instructions = new ArrayList<>();
    }

    @Override
    public void enterInstruction(SomeLanguageParser.InstructionContext ctx) {
      String instructionName = ctx.getText();
      instructions.add(new Instruction(instructionName));
    }

    public Collection<Instruction> getInstructions() {
      return instructions;
    }
  }

  class MethodListener extends SomeLanguageBaseListener {
    private Collection<Method> methods;

    public MethodListener() {
      methods = new ArrayList<>();
    }

    @Override
    public void enterMethod(SomeLanguageParser.MethodContext ctx) {
      String methodName = ctx.methodName().getText();
      InstructionListener instructionListener = new InstructionListener();
      ctx.instruction().forEach(instruction ->
          instruction.enterRule(instructionListener)
      );
      Collection<Instruction> instructions = instructionListener.getInstructions();
      methods.add(new Method(methodName, instructions));
    }

    public Collection<Method> getMethods() {
      return methods;
    }
  }

  class ClassListener extends SomeLanguageBaseListener {
    private Class parsedClass;

    @Override
    public void enterClassDeclaration(SomeLanguageParser.ClassDeclarationContext ctx) {
      String className = ctx.className().getText();
      MethodListener methodListener = new MethodListener();

      ctx.method().forEach((x)-> {
        x.enterRule(methodListener);
      });

      Collection<Method> methods = methodListener.getMethods();

      parsedClass = new Class(className,methods);
    }

    public Class getParsedClass() {
      return parsedClass;
    }
  }
}
